/****************************************************************************************************************************\
* Arduino project "Nodo UserPlugin Barebone TestEngine" © Copyright 2013 Martinus van den Broek 
* Work based on Arduino project "Nodo" © Copyright 2012 Paul Tonkes 
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License 
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
*
* You received a copy of the GNU General Public License along with this program in tab '_COPYING'.
******************************************************************************************************************************
******************************************************************************************************************************
* Purpose        : Arduino Source code for Nodo BareBone TestEngine based on Atmel ATMega 328
* Version        : R13
* Date           : 20130106
* 
* This code turns an Atmel ATMega 328 chip into a Nodo compatible device with RF send/receive function only
* There is very limited support for an eventlist, only to serve some UserPlugin commands.
* This version is specifically build to serve as a "multiple UserPlugin" test engine with minimum code size.
* This code is not of any use wihout running a full Nodo Domotica Unit within RF range to receive data.
*****************************************************************************************************************************/

#define UNIT_NODO    8 
#define USER_PLUGIN       "UserPlugin"                             // Commando naam waarmee de plugin kan worden aangeroepen. Remarken met // als UserPluging niet nodig is

/* Following functions are implemented in this barebone test version:
Reset
command2event
Nodo_2_RawSignal
RawSendRF
GetEvent_IRRF (RF only!)
AnalyzeRawSignal (Nodo messages only!)
RawSignal_2_Nodo
WaitFreeRF
WaitForChangeState
FetchSignal
ExecuteCommand (only 2 commands: UserPlugin and SendUserEvent)
ProcessEvent (vary limited!)
EventList_Read
TransmitCode (RF only!)
PrintEvent
GetArgv
Float2event
*/

#define NODO_BUILD          491

#include <Wire.h>
#include <EEPROM.h>

#define RAW_BUFFER_SIZE                250 // Maximaal aantal te ontvangen bits*2, Nodo messages only!
#define SIGNAL_TYPE_NODO                 1 // Type ontvangen of te verzenden signaal in de eventcode

#define CMD_EVENTLIST_ERASE             12
#define CMD_USERPLUGIN                  26
#define CMD_TIMER_SET_MIN               29
#define CMD_VARIABLE_SET                35
#define CMD_SEND_USEREVENT              44
#define CMD_STATUS                      54
#define CMD_REBOOT                      79
#define CMD_USEREVENT                  100
#define CMD_TIMER_EVENT                113
#define CMD_VARIABLE_EVENT             115
#define CMD_BOOT_EVENT                 119
#define VALUE_OFF                      150 
#define VALUE_ON                       151 // Deze waarde MOET groter dan 16 zijn.
#define VALUE_SOURCE_SERIAL            157
#define VALUE_SOURCE_RF                155
#define VALUE_SOURCE_EVENTLIST         159
#define VALUE_SOURCE_TIMER             161
#define VALUE_DIRECTION_INPUT          168
#define VALUE_DIRECTION_OUTPUT         169
#define VALUE_DIRECTION_INTERNAL       170
#define VALUE_ALL                      172 // Deze waarde MOET groter dan 16 zijn.

#define PIN_RF_TX_VCC                4 // +5 volt / Vcc spanning naar de zender.
#define PIN_RF_TX_DATA               5 // data naar de zender
#define PIN_RF_RX_DATA               2 // Op deze input komt het 433Mhz-RF signaal binnen. LOW bij geen signaal.
#define PIN_RF_RX_VCC               12 // Spanning naar de ontvanger via deze pin.
#define RF_REPEATS                   5 // aantal herhalingen van een code binnen één RF reeks
#define EVENTLIST_MAX               50 // aantal events dat de lijst bevat in het EEPROM geheugen. Iedere regel in de eventlist heeft 8 bytes nodig. eerste adres is 0
#define USER_VARIABLES_MAX          15 // aantal beschikbare gebruikersvariabelen voor de user.
#define BAUD_RATE                19200 // Serial communication baud rate

#define NODO_PULSE_0               500 // PWM: Tijdsduur van de puls bij verzenden van een '0' in uSec.
#define NODO_PULSE_MID            1000 // PWM: Pulsen langer zijn '1'
#define NODO_PULSE_1              1500 // PWM: Tijdsduur van de puls bij verzenden van een '1' in uSec. (3x NODO_PULSE_0)
#define NODO_SPACE                 500 // PWM: Tijdsduur van de space tussen de bitspuls bij verzenden van een '1' in uSec.   
#define Loop_INTERVAL_3           1000 // tijdsinterval in ms. voor achtergrondtaken langzame verwerking

#define INPUT_BUFFER_SIZE           32 // Buffer waar de karakters van de seriele poort in worden opgeslagen.
#define SIGNAL_ANALYZE_SHARPNESS    50 // Scherpte c.q. foutmarge die gehanteerd wordt bij decoderen van RF/IR signaal.
#define SIGNAL_TIMEOUT_RF         5000 // na deze tijd in uSec. wordt één RF signaal als beëindigd beschouwd.
#define SIGNAL_TIMEOUT_IR        10000 // na deze tijd in uSec. wordt één IR signaal als beëindigd beschouwd.
#define SIGNAL_REPEAT_TIME        1000 // Tijd waarbinnen hetzelfde event niet nogmaals via RF of IR mag binnenkomen. Onderdrukt ongewenste herhalingen van signaal
#define RF_REPEATS                   5 // aantal herhalingen van een code binnen één RF reeks
#define IR_REPEATS                   5 // aantal herhalingen van een code binnen één IR reeks
#define MIN_PULSE_LENGTH           100 // pulsen korter dan deze tijd uSec. worden als stoorpulsen beschouwd.
#define MIN_RAW_PULSES              32 // =16 bits. Minimaal aantal ontvangen bits*2 alvorens cpu tijd wordt besteed aan decodering, etc. Zet zo hoog mogelijk om CPU-tijd te sparen en minder 'onzin' te ontvangen.
#define SHARP_TIME                 750 // tijd in milliseconden dat de nodo gefocust moet blijven luisteren naar één dezelfde poort na binnenkomst van een signaal
#define RECEIVER_STABLE            750 // Tijd die de RF ontvanger nodig heeft om na inschakelen voedspanning signalen op te kunnen vangen. 
#define DELAY_BEFORE_SEND         2000 // Korte pauze voor verzenden van een event, direct nadat er één is ontvangen van een andere Nodo. Alleen actief tijdens [WaitFreeRF On]
#define USER_VARIABLES_RANGE_MIN  -100 // Laagste waarde die door de gerbuiker kan worden opgeslagen in een gebruikersvariabele
#define USER_VARIABLES_RANGE_MAX   100 // Hoogste waarde die door de gerbuiker kan worden opgeslagen in een gebruikersvariabele
#define TIMER_MAX                    8 // aantal beschikbare timers voor de user, gerekend vanaf 1

struct SettingsStruct
  {
  int     Version;        
  byte    Unit;
  byte    Debug;
  }Settings;
  
struct RawsignalStruct
  {
  unsigned int Pulses[RAW_BUFFER_SIZE+2];                   // Tabel met de gemeten pulsen in microseconden. eerste waarde [0] wordt NIET gebruikt. (legacy redenen).
  byte Source;                                              // Bron waar het signaal op is binnengekomen.
  int Number;                                               // aantal bits, maal twee omdat iedere bit een mark en een space heeft.
  int Key;                                                  // sleutel waaronder de pulsenreeks op SDCard opgeslagen moet worden.
  byte Type;                                                // Type signaal dan ontvangen is.
  unsigned long Timer;                                      // Tijdstip millis() waarop event is binnengekomen.
  }RawSignal;

uint8_t RFbit,RFport;                                       // t.b.v. verwerking IR/FR signalen.
char InputBuffer_Serial[INPUT_BUFFER_SIZE+2];               // Buffer voor input Seriele data
float UserVar[USER_VARIABLES_MAX];                          // Gebruikers variabelen
unsigned long UserTimer[TIMER_MAX];                         // Timers voor de gebruiker.
unsigned long LoopIntervalTimer_3=0L;                       // Timer voor periodieke verwerking.

/*********************************************************************************************\
 * Setup stuff
\*********************************************************************************************/
void setup()
{
  Settings.Unit = UNIT_NODO;
  Settings.Debug = VALUE_OFF;
  pinMode(PIN_RF_RX_DATA, INPUT);
  pinMode(PIN_RF_TX_DATA, OUTPUT);
  pinMode(PIN_RF_TX_VCC,  OUTPUT);
  pinMode(PIN_RF_RX_VCC,  OUTPUT);
  digitalWrite(PIN_RF_RX_VCC,HIGH);   // Spanning naar de RF ontvanger aan.
  digitalWrite(PIN_RF_RX_DATA,INPUT_PULLUP);  // schakel pull-up weerstand in om te voorkomen dat er rommel binnenkomt als pin niet aangesloten.
  RFbit=digitalPinToBitMask(PIN_RF_RX_DATA);
  RFport=digitalPinToPort(PIN_RF_RX_DATA);
  Serial.begin(BAUD_RATE);
  Serial.println("Nodo BareBone");
  #ifdef USER_PLUGIN
    UserPlugin_Init();
  #endif
  ProcessEvent(command2event(Settings.Unit, CMD_BOOT_EVENT,Settings.Unit,0),0,0,0,0);
}

/*********************************************************************************************\
 * Main loop
\*********************************************************************************************/
void loop()
{
  int x;
  unsigned long Content=0L;                                   // Ontvangen event van RF, IR, ... Tevens buffer voor het vorige ontvangen Event
  unsigned long EventTimeCodePrevious; 
  unsigned long StaySharpTimer;                               // Timer die er voor zorgt dat bij communicatie via een kanaal de focus hier ook enige tijd op blijft

  // Check RF events
  if(GetEvent_IRRF(&Content,&x))
  {
    #ifdef USER_PLUGIN
      UserPlugin_Receive(Content);
    #endif
    PrintEvent(Content,VALUE_DIRECTION_INPUT,x);
    ProcessEvent(Content,VALUE_DIRECTION_INPUT,x,0,0);
  }
  
  // Check serial input
  #ifdef USER_PLUGIN
    UserPlugin_MiniMMI();
  #endif

  // TIMER: **************** Genereer event als één van de Timers voor de gebruiker afgelopen is ***********************    
  for(x=0;x<TIMER_MAX;x++)
    {
    if(UserTimer[x]!=0L)// als de timer actief is
      {
      if(UserTimer[x]<millis()) // als de timer is afgelopen.
        {
          UserTimer[x]=0L;// zet de timer op inactief.
          Content=command2event(Settings.Unit, CMD_TIMER_EVENT,x+1,0);
          ProcessEvent(Content,VALUE_DIRECTION_INTERNAL,VALUE_SOURCE_TIMER,0,0);      // verwerk binnengekomen event.
        }
      }
    }
    
  // loop periodiek langs de userplugin
  if(LoopIntervalTimer_3<millis())// lange interval: Iedere seconde.
    {
      LoopIntervalTimer_3=millis()+Loop_INTERVAL_3; // reset de timer  
      #ifdef USER_PLUGIN
        UserPlugin_Periodically();
      #endif 
    } 
}

/*********************************************************************************************\
 * Reset function
\*********************************************************************************************/
void(*Reset)(void)=0;

 /*********************************************************************************************\
 * Bouw een Code op uit commando, data1 en data2
 \*********************************************************************************************/
unsigned long command2event(byte Unit, byte Command, byte Par1, byte Par2)
    {
    return ((unsigned long)SIGNAL_TYPE_NODO)<<28  | 
           ((unsigned long)Unit)<<24            | 
           ((unsigned long)Command)<<16           | 
           ((unsigned long)Par1)<<8               | 
            (unsigned long)Par2;
    }

/*********************************************************************************************\
 * Calculate the RAW pulses for a 32-bit Nodo-code and store into RawSignal buffer
\*********************************************************************************************/
void Nodo_2_RawSignal(unsigned long Code)
{
  byte BitCounter,y=1;

  // begin met een startbit. 
  RawSignal.Pulses[y++]=NODO_PULSE_1*2; 
  RawSignal.Pulses[y++]=NODO_SPACE*4;

  // de rest van de bits LSB als eerste de lucht in
  for(BitCounter=0; BitCounter<=31; BitCounter++)
  {
    if(Code>>BitCounter&1)
      RawSignal.Pulses[y++]=NODO_PULSE_1; 
    else
      RawSignal.Pulses[y++]=NODO_PULSE_0;   
    RawSignal.Pulses[y++]=NODO_SPACE;   
  }
  RawSignal.Pulses[y-1]=NODO_PULSE_1*10; // pauze tussen de pulsreeksen
  RawSignal.Pulses[0]=66; //  1 startbit bestaande uit een pulse/space + 32-bits is 64 pulse/space = totaal 66
}

/*********************************************************************************************\
 * Send Nodo message through RF transmitter
\*********************************************************************************************/
void RawSendRF(void)
  {
  int x;
    
  digitalWrite(PIN_RF_RX_VCC,LOW);   // Spanning naar de RF ontvanger uit om interferentie met de zender te voorkomen.
  digitalWrite(PIN_RF_TX_VCC,HIGH); // zet de 433Mhz zender aan

  delay(5);// kleine pause om de zender de tijd te geven om stabiel te worden 
  
  for(byte y=0; y<RF_REPEATS; y++) // herhaal verzenden RF code
    {
    x=1;
    while(x<=RawSignal.Number)
      {
      digitalWrite(PIN_RF_TX_DATA,HIGH); // 1
      delayMicroseconds(RawSignal.Pulses[x++]); 
      digitalWrite(PIN_RF_TX_DATA,LOW); // 0
      delayMicroseconds(RawSignal.Pulses[x++]); 
      }
    }
  
  digitalWrite(PIN_RF_TX_VCC,LOW); // zet de 433Mhz zender weer uit
  digitalWrite(PIN_RF_RX_VCC,HIGH); // Spanning naar de RF ontvanger weer aan.

  }

boolean GetEvent_IRRF(unsigned long *Content, int *Port)
  {
  unsigned long Checksum=0L;                           // Als gelijk aan Event dan tweemaal dezelfde code ontvangen: checksum funktie.
  unsigned long StaySharpTimer=millis();                      // timer die start bij ontvangen van een signaal. Dwingt om enige tijd te luisteren naar dezelfde poort.
  static unsigned long Previous;

  // RF: *************** kijk of er data start op RF en genereer een event als er een code ontvangen is **********************
  do
    {
    if((*portInputRegister(RFport)&RFbit)==RFbit)// Kijk if er iets op de RF poort binnenkomt. (Pin=HOOG als signaal in de ether). 
      {
      if(FetchSignal(PIN_RF_RX_DATA,HIGH,SIGNAL_TIMEOUT_RF))// Als het een duidelijk RF signaal was
        {
        *Content=AnalyzeRawSignal(); // Bereken uit de tabel met de pulstijden de 32-bit code. 
        if(*Content)// als AnalyzeRawSignal een event heeft opgeleverd
          {
          StaySharpTimer=millis()+SHARP_TIME;          
          if(*Content==Checksum) // tweemaal hetzelfde event ontvangen
            {
            if((RawSignal.Timer+SIGNAL_REPEAT_TIME)<millis())
              Previous=0L;
            if(Previous!=Checksum)
              {
              RawSignal.Source=VALUE_SOURCE_RF;
              *Port=VALUE_SOURCE_RF;
              RawSignal.Timer=millis();
              Previous=Checksum;
              return true;
              }
            }
          Checksum=*Content;
          }
        }
      }
    }while(StaySharpTimer>millis());
  return false;
  }
  
/*********************************************************************************************\
 * AnalyzeRawSignal
\*********************************************************************************************/
unsigned long AnalyzeRawSignal(void)
  {
  unsigned long Code=0L;
  
  if(RawSignal.Number==RAW_BUFFER_SIZE)return 0L;     // Als het signaal een volle buffer beslaat is het zeer waarschijnlijk ruis of ongeldig signaal

  #ifdef USER_PLUGIN
  Code=RawSignal_2_UserPlugin(RawSignal.Source);
  if(Code==1)
    return 0;
  else if(Code>1)
    return Code;
  #endif
  
  Code=RawSignal_2_Nodo();
      
  return Code;   
  }

/*********************************************************************************************\
* Deze routine berekent de uit een RawSignal een NODO code
* Geeft een false retour als geen geldig NODO signaal
\*********************************************************************************************/
unsigned long RawSignal_2_Nodo(void)
  {
  unsigned long bitstream=0L;
  int x,y,z;

  // NODO signaal bestaat uit start bit + 32 bits. Als ongelijk aan 66, dan geen Nodo signaal
  if(RawSignal.Number!=66)return 0L;

  // 0=aantal, 1=startpuls, 2=space na startpuls, 3=1e pulslengte. Dus start loop met drie.
  z=0;
  for(x=3;x<=RawSignal.Number;x+=2)
    {
    if(RawSignal.Pulses[x]>NODO_PULSE_MID)      
      bitstream|=(long)(1L<<z); //LSB in signaal wordt  als eerste verzonden
    z++;
    }

  if(((bitstream>>28)&0xf) == SIGNAL_TYPE_NODO)// is het type-nibble uit het signaal gevuld met de aanduiding NODO.
    {
    RawSignal.Type=SIGNAL_TYPE_NODO;
    return bitstream;
    }

  return 0L;  
  }
 /**********************************************************************************************\
 * Deze functie wacht totdat de 433 band vrij is of er een timeout heeft plaats gevonden 
 * Window en delay tijd in milliseconden
 \*********************************************************************************************/
# define WAITFREERF_TIMEOUT             30000 // tijd in ms. waarna het wachten wordt afgebroken als er geen ruimte in de vrije ether komt

void WaitFreeRF(void)
  {
  unsigned long Timer, TimeOutTimer;  

  // eerst de 'dode' wachttijd die afhankt van het unitnummer. Dit voorkomt dat alle units exact op hetzelfde moment gaan zenden als de ether vrij is.
  delay((Settings.Unit-1)*100);
    
  // dan kijken of de ether vrij is.
  Timer=millis()+350; // reset de timer.
  TimeOutTimer=millis()+WAITFREERF_TIMEOUT; // tijd waarna de routine wordt afgebroken in milliseconden

  while(Timer>millis() && TimeOutTimer>millis())
    {
    if((*portInputRegister(RFport)&RFbit)==RFbit)// Kijk if er iets op de RF poort binnenkomt. (Pin=HOOG als signaal in de ether). 
      {
      if(FetchSignal(PIN_RF_RX_DATA,HIGH,SIGNAL_TIMEOUT_RF))// Als het een duidelijk signaal was
        Timer=millis()+350; // reset de timer weer.
      }
    }
//  Led(RED);
  }


 /**********************************************************************************************\
 * Wacht totdat de pin verandert naar status state. Geeft de tijd in uSec. terug. 
 * Als geen verandering, dan wordt na timeout teruggekeerd met de waarde 0L
 \*********************************************************************************************/
unsigned long WaitForChangeState(uint8_t pin, uint8_t state, unsigned long timeout)
	{
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);
	uint8_t stateMask = (state ? bit : 0);
	unsigned long numloops = 0; // keep initialization out of time critical area
	unsigned long maxloops = microsecondsToClockCycles(timeout) / 19;
	
	// wait for the pulse to stop. One loop takes 19 clock-cycles
	while((*portInputRegister(port) & bit) == stateMask)
		if (numloops++ == maxloops)
			return 0;//timeout opgetreden
	return clockCyclesToMicroseconds(numloops * 19 + 16); 
	}
 /**********************************************************************************************\
 * Haal de pulsen en plaats in buffer. Op het moment hier aangekomen is de startbit actief.
 * bij de TSOP1738 is in rust is de uitgang hoog. StateSignal moet LOW zijn
 * bij de 433RX is in rust is de uitgang laag. StateSignal moet HIGH zijn
 * 
 \*********************************************************************************************/
boolean FetchSignal(byte DataPin, boolean StateSignal, int TimeOut)
  {
  int RawCodeLength=1;
  unsigned long PulseLength=0;
  
  do{// lees de pulsen in microseconden en plaats deze in een tijdelijke buffer
    PulseLength=WaitForChangeState(DataPin, StateSignal, TimeOut);

    if(PulseLength<MIN_PULSE_LENGTH)
      return false;

    RawSignal.Pulses[RawCodeLength++]=PulseLength;
    PulseLength=WaitForChangeState(DataPin, !StateSignal, TimeOut);
    RawSignal.Pulses[RawCodeLength++]=PulseLength;
    }while(RawCodeLength<RAW_BUFFER_SIZE && PulseLength!=0);// Zolang nog niet alle bits ontvangen en er niet vroegtijdig een timeout plaats vindt

  if(RawCodeLength>=MIN_RAW_PULSES)
    {
    RawSignal.Number=RawCodeLength-1;
    return true;
    }
  RawSignal.Number=0;
  return false;
  }

/*********************************************************************************************\
 * Deze functie checked of de code die ontvangen is een uitvoerbare opdracht is/
 * Als het een correct commando is wordt deze uitgevoerd en 
 * true teruggegeven. Zo niet dan wordt er een 'false' retour gegeven.
\*********************************************************************************************/
#define EventPartType(Event)      ((Event>>28)&0xf)
#define EventPartUnit(Event)      ((Event>>24)&0xf)
#define EventPartCommand(Event)   ((Event>>16)&0xff)
#define EventPartPar1(Event)      ((Event>>8)&0xff)
#define EventPartPar2(Event)      (Event&0xff)
#define EventPart4Bit(Event)      ((Event>>12)&0xf)
#define EventPart10Bit(Event)     (Event&0x3ff)
#define EventPart16Bit(Event)     (Event&0xffff)

boolean ExecuteCommand(unsigned long InEvent, int Src, unsigned long PreviousInEvent, int PreviousSrc)
  {
    
  byte Command      = EventPartCommand(InEvent);
  byte Par1         = EventPartPar1(InEvent);
  byte Par2         = EventPartPar2(InEvent);
  
  switch(Command)
    {
    case CMD_TIMER_SET_MIN:
      // Par1=timer, Par2=minuten. Timers werken op een resolutie van seconden maar worden door de gebruiker ingegeven in minuten        
      UserTimer[Par1-1]=millis()+(unsigned long)Par2*60000L;
      break;
   
    case CMD_SEND_USEREVENT:
      // Voeg Unit=0 want een UserEvent is ALTIJD voor ALLE Nodo's. Verzend deze vervolgens.
      TransmitCode(command2event(Settings.Unit, CMD_USEREVENT,Par1,Par2),VALUE_ALL);
      break;

    case CMD_USERPLUGIN:
      #ifdef USER_PLUGIN
        UserPlugin_Command(Par1,Par2);
      #endif
      break;        

    case CMD_STATUS:
      if (Par1 == CMD_VARIABLE_SET)
        {
          TransmitCode(float2event(UserVar[Par2-1], Par2-1, CMD_VARIABLE_EVENT),VALUE_ALL); // verzend als event
        }
      break;

    case CMD_REBOOT:
      Reset();
      break;        

    case CMD_EVENTLIST_ERASE:
      int baseaddress=sizeof(struct SettingsStruct);
      for (int x=0; x < (EVENTLIST_MAX*8); x++)
        {
          EEPROM.write(baseaddress+x, 0);
        }
      break;        

    }
  }

/**********************************************************************************************\
 * Voert alle relevante acties in de eventlist uit die horen bij het binnengekomen event
 * Doorlopen van een volledig gevulde eventlist duurt ongeveer 15ms inclusief printen naar serial
 * maar exclusief verwerking n.a.v. een 'hit' in de eventlist
\*********************************************************************************************/

void ProcessEvent(unsigned long IncommingEvent, byte Direction, byte Port, unsigned long PreviousContent, byte PreviousPort)
{
  unsigned long Event_1, Event_2;
  int x;
  byte Cmd=(IncommingEvent>>16)&0xff; // Command
  byte Par1=(IncommingEvent>>8 )&0xff; // Par1
  byte Par2=(IncommingEvent    )&0xff; // Par2

  for(x=1; x<=EVENTLIST_MAX; x++)
  {
    Eventlist_Read(x,&Event_1,&Event_2);
    if ((IncommingEvent & 0xffffff) == (Event_1 &0xffffff))
    {
      PrintEvent(Event_2,VALUE_DIRECTION_INTERNAL, VALUE_SOURCE_EVENTLIST);
      ExecuteCommand(Event_2, VALUE_SOURCE_EVENTLIST,0,0);
    }
  }
}

/**********************************************************************************************\
 * Revision 01, 09-12-2009, P.K.Tonkes@gmail.com
\*********************************************************************************************/
boolean Eventlist_Read(int address, unsigned long *Event, unsigned long *Action)// LET OP: eerste adres=1
{
  if(address>EVENTLIST_MAX)return(false);

  address--;// echte adressering begint vanaf nul. voor de user vanaf 1.
  address=address*8+sizeof(struct SettingsStruct);     // Eerste deel van het EEPROM geheugen is voor de settings. Reserveer deze bytes. Deze niet te gebruiken voor de Eventlist!

  *Event  =((unsigned long)(EEPROM.read(address++))) << 24;
  *Event|= ((unsigned long)(EEPROM.read(address++))) << 16;
  *Event|= ((unsigned long)(EEPROM.read(address++))) <<  8;
  *Event|= ((unsigned long)(EEPROM.read(address++)))      ;
  *Action =((unsigned long)(EEPROM.read(address++))) << 24;
  *Action|=((unsigned long)(EEPROM.read(address++))) << 16;
  *Action|=((unsigned long)(EEPROM.read(address++))) <<  8;
  *Action|=((unsigned long)(EEPROM.read(address  )))      ;
  if(*Event==0L)
    return(false);
  else
    return(true);
}

/**********************************************************************************************\
* verzendt een event en geeft dit tevens weer op SERIAL
* als het Event gelijk is aan 0L dan wordt alleen de huidige inhoud van de buffer als RAW
* verzonden.
\**********************************************************************************************/
boolean TransmitCode(unsigned long Event, byte Dest)
  {  
  int x;
  byte SignalType=(Event>>28) & 0x0f;
  byte Command   =(Event>>16) & 0xff;
  
  WaitFreeRF();  

  if(SignalType==SIGNAL_TYPE_NODO) Nodo_2_RawSignal(Event);

  PrintEvent(Event,VALUE_DIRECTION_OUTPUT, VALUE_SOURCE_RF);

  while((RawSignal.Timer+DELAY_BEFORE_SEND) > millis());

  RawSendRF();

  }
  
/*********************************************************************************************\
 * Print een event: debug mode Nodo-Small
 \*********************************************************************************************/
void PrintEvent(unsigned long Content, byte Direction, byte Port)
  {
  Serial.print(Direction);
  Serial.print(",");
  Serial.print(Port);
  Serial.print(",0x");
  Serial.println(Content,HEX);
  } 

unsigned long float2event(float f, byte port, byte cmd)
  {
  boolean high=false;
  boolean sign=false;
  long wi;

  if(f>=USER_VARIABLES_RANGE_MIN && f<= USER_VARIABLES_RANGE_MAX)
    {
    wi=int(f * 100);
    
    if(wi<0)
      {
      sign=true;
      wi=-wi;
      }
      
    if(wi>=1000)
      {
      high=true;
      wi=wi/10;
      }
  
    return ((unsigned long)SIGNAL_TYPE_NODO)<<28   |
           ((unsigned long)Settings.Unit)<<24      | 
           ((unsigned long)cmd)<<16                |
           ((unsigned long)port)<<12               |
           ((unsigned long)high)<<11               |
           ((unsigned long)sign)<<10               |
           ((unsigned long)(wi & 0x3ff));
    }
  else
    return 0L;  
  }

